﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Data;

namespace ProgNetComponents.Data
{
    public abstract class BaseDBUpdateProc
    {
        public virtual DataManager Manager
        {
            get;
            set;
        }

        public abstract bool Execute();
    }
    public class DBUpdateProc : BaseDBUpdateProc
    { 
        public override DataManager Manager
        {
            get
            {
                return base.Manager;
            }
            set
            {
                if (value != null)
                {
                    value.AddTableDef(Table);
                    value.FillTable(Table);
                }
                base.Manager = value;
            }
        }

        public string[] KeyKolumns
        {
            get;
            set;
        }
        private List<ObjectKeyValuePairWithType> _Parameters = new List<ObjectKeyValuePairWithType>();
        
        public string TableName
        {
            get;
            set;
        }

        public bool ForceUpdate
        {
            get;
            set;
        }
        private DataTable _Table;
        private DataTable Table
        {
            get
            {
                if (_Table == null)
                {
                    _Table = new DataTable(TableName);
                    foreach (ObjectKeyValuePairWithType kvp in Parameters)
                    {
                       DataColumn col = _Table.Columns.Add(kvp.Key, kvp.Type.IsNull() ? typeof(string) : kvp.Type);
                       if (kvp.Value.IsNotNull())
                           col.DefaultValue = kvp.Value;
                    }
                    List<DataColumn> pk = new List<DataColumn>();
                    foreach (string k in KeyKolumns)
                    {
                        pk.Add(_Table.Columns[k]);
                    }
                    _Table.PrimaryKey = pk.ToArray();
                }
                return _Table;
            }
        }
        
        private DataTable _CloneTable;
        private DataTable CloneTable
        {
            get
            {
                if (_CloneTable == null)
                    _CloneTable = Table.Clone();
                return _CloneTable;
            }
        }
        
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public List<ObjectKeyValuePairWithType> Parameters
        {
            get { return _Parameters; }
        }
        public DataRow CreateRow()
        {
            return CloneTable.NewRow();
        }

        public void AddRow(params object[] parameters)
        {
            DataRow r = CreateRow();
            for (int i=0; i < parameters.Length; i++)
                r[i] = parameters[i].IsNull() ? DBNull.Value : parameters[i];
            CloneTable.Rows.Add(r);
        }
        public void AddRow(DataRow row)
        {
            CloneTable.Rows.Add(row);
        }

        public event EventHandler<UpdateRowEventArgs> RowUpdateError;
        protected virtual void OnErrorUpdatingRow(DataRow r)
        {
            if (RowUpdateError != null)
                RowUpdateError(this, new UpdateRowEventArgs(r));
        }

        public override bool Execute()
        {
            Manager.AutoCommit = true;
            bool result = true;
            Table.Merge(CloneTable);
            List<DataRow> rowsToUpdate = new List<DataRow>();
            DataRow[] rows  = Table.GetRealChangedRows();
            foreach (DataRow r in rows)
            {
                if (r.RowState != DataRowState.Modified || ForceUpdate)
                {
                    bool tmp = Manager.Update(new DataRow[]{r});
                    if (!tmp)
                    {
                        if (!Manager.GetAutoLogExceptions)
                            Manager.LastException.Log();
                        OnErrorUpdatingRow(r);
                    }
                    result &= tmp;
                }
            }
            return result;
        }
    }
    public class DBScriptProc : BaseDBUpdateProc
    {
        private Dictionary<int, string> Scripts = new Dictionary<int, string>();
        private Dictionary<string, string> FalseScripts = new Dictionary<string, string>();
        /// <summary>
        /// Scripts from this number will be executed
        /// </summary>
        public int StartFrom
        {
            get;
            set;
        }
        /// <summary>
        /// Gets scripts that had errors
        /// </summary>
        /// <returns></returns>
        public Dictionary<string, string> GetFalseScripts()
        {
            return FalseScripts;
        }
        public int ScriptsCount
        {
            get { return Scripts.Count; }
        }
        public void Add(string script)
        {
            Scripts.Add(Scripts.Count, script);
        }
        public event EventHandler<ScriptExecuteErrorEventArgs> ExecuteError;

        protected virtual void OnErrorExecuteScript(int id, string script)
        {
            if (ExecuteError != null)
                ExecuteError(this, new ScriptExecuteErrorEventArgs(id, script));
        }
        public override bool Execute()
        {
            Manager.AutoCommit = true;
            bool result = true;
            if (StartFrom < 0)
                StartFrom = 0;
            for (int i=StartFrom; i < Scripts.Count; i++)
            {
                Manager.ExecuteNonQuery(Scripts[i]);
                if (Manager.LastException!=null)
                {
                    FalseScripts.Add(Scripts[i], Manager.LastException.IsNotNull()? Manager.LastException.Message:"");
                    if (!Manager.GetAutoLogExceptions) 
                        Manager.LastException.Log();
                    OnErrorExecuteScript(i, Scripts[i]);
                    result = false;
                }
            }
            return result;
        }
    }
    public class UpdateRowEventArgs : EventArgs
    {
        public UpdateRowEventArgs(DataRow row)
        {
            _Row = row;
        }
        private DataRow _Row;
        public DataRow Row
        {
            get { return _Row; }
        }
    }
    public class ScriptExecuteErrorEventArgs : EventArgs
    {
        public ScriptExecuteErrorEventArgs(int idx, string script)
        {
            _Index = idx;
            _Script = script;
        }
        private int _Index;
        public int Index
        {
            get { return _Index; }
        }
        private string _Script;
        public string Script
        {
            get { return _Script; }
        }
    }
    public class ObjectKeyValuePairWithType : ObjectKeyValuePair
    {
        public ObjectKeyValuePairWithType(string key = "", object value = null, Type t = null)
            : base(key, value)
        {
            this.Type = t;
        }
        private Type _Type = null;
        
        public Type Type
        {
            get{if(Value.IsNotNull())
                return Value.GetType();
                return _Type;}
            set{
                _Type = value;}
        }
    }
}
